from vrpsc.Util.util import *
from vrpsc.Util.load_data import *
import random
from vrpsc.Util.Solution import Solution
import math
from vrpsc.Util.generate_init_solution import generate_solution_nearest,generate_solution_greedy,generate_solution_random
from vrpsc.ALNS import _calculate_and_package_metrics

'''入侵杂草算法参数'''
POP_INITIAL_SIZE = 80     # 初始种群大小
POP_MAX_SIZE = 100         # 种群最大大小
S_MAX = 20                # seed最大数
S_MIN = 1                 # seed最小数



def neighbor_insertion(solution):
    task = random.randint(1, solution.task_num)
    sequence_map = solution.get_sequence_map()
    path_init_task_map = solution.get_path_init_task_map()

    remove_(sequence_map, path_init_task_map, task)

    parent_positions = get_all_position(sequence_map, path_init_task_map, task, 'parent')

    parent_position = random.sample(parent_positions, 1)[0]
    insert_(sequence_map, path_init_task_map, task, parent_position, 'parent')

    child_positions = get_feasible_insert_position(sequence_map, path_init_task_map, task, 'child')
    child_position = random.sample(child_positions, 1)[0]
    insert_(sequence_map, path_init_task_map, task, child_position, 'child')
    return Solution(solution.instance, sequence_map, path_init_task_map)


def neighbor_swap(solution):
    sequence_map = solution.get_sequence_map()
    path_init_task_map = solution.get_path_init_task_map()
    task_list = list(range(1, solution.task_num + 1))
    task_1 = random.sample(task_list, 1)[0]
    task_list.remove(task_1)
    task_2 = random.sample(task_list, 1)[0]
    '''如果任务是某个AGV的第一个任务，更新path_init_task_map'''
    if sequence_map[task_1]['parent_pre_task'] == 0:
        path_init_task_map[sequence_map[task_1]['parent']] = task_2

    if sequence_map[task_1]['child_pre_task'] == 0:
        path_init_task_map[sequence_map[task_1]['child']] = task_2

    if sequence_map[task_2]['parent_pre_task'] == 0:
        path_init_task_map[sequence_map[task_2]['parent']] = task_1

    if sequence_map[task_2]['child_pre_task'] == 0:
        path_init_task_map[sequence_map[task_2]['child']] = task_1

    '''交换task_1和task_2'''
    task_1_parent = sequence_map[task_1]['parent']
    task_1_parent_pre_task = sequence_map[task_1]['parent_pre_task']
    task_1_parent_next_task = sequence_map[task_1]['parent_next_task']
    task_1_child = sequence_map[task_1]['child']
    task_1_child_pre_task = sequence_map[task_1]['child_pre_task']
    task_1_child_next_task = sequence_map[task_1]['child_next_task']

    if sequence_map[task_1]['parent_pre_task'] != 0 and sequence_map[task_1]['parent_pre_task'] != task_2:
        sequence_map[sequence_map[task_1]['parent_pre_task']]['parent_next_task'] = task_2

    if sequence_map[task_1]['parent_next_task'] != 0 and sequence_map[task_1]['parent_next_task'] != task_2:
        sequence_map[sequence_map[task_1]['parent_next_task']]['parent_pre_task'] = task_2

    if sequence_map[task_1]['child_pre_task'] != 0 and sequence_map[task_1]['child_pre_task'] != task_2:
        sequence_map[sequence_map[task_1]['child_pre_task']]['child_next_task'] = task_2

    if sequence_map[task_1]['child_next_task'] != 0 and sequence_map[task_1]['child_next_task'] != task_2:
        sequence_map[sequence_map[task_1]['child_next_task']]['child_pre_task'] = task_2

    ''''''

    if sequence_map[task_2]['parent_pre_task'] != 0 and sequence_map[task_2]['parent_pre_task'] != task_1:
        sequence_map[sequence_map[task_2]['parent_pre_task']]['parent_next_task'] = task_1

    if sequence_map[task_2]['parent_next_task'] != 0 and sequence_map[task_2]['parent_next_task'] != task_1:
        sequence_map[sequence_map[task_2]['parent_next_task']]['parent_pre_task'] = task_1

    if sequence_map[task_2]['child_pre_task'] != 0 and sequence_map[task_2]['child_pre_task'] != task_1:
        sequence_map[sequence_map[task_2]['child_pre_task']]['child_next_task'] = task_1

    if sequence_map[task_2]['child_next_task'] != 0 and sequence_map[task_2]['child_next_task'] != task_1:
        sequence_map[sequence_map[task_2]['child_next_task']]['child_pre_task'] = task_1

    sequence_map[task_1]['parent'] = sequence_map[task_2]['parent']
    if sequence_map[task_2]['parent_pre_task'] == task_1:
        sequence_map[task_1]['parent_pre_task'] = task_2
    else:
        sequence_map[task_1]['parent_pre_task'] = sequence_map[task_2]['parent_pre_task']

    if sequence_map[task_2]['parent_next_task'] == task_1:
        sequence_map[task_1]['parent_next_task'] = task_2
    else:
        sequence_map[task_1]['parent_next_task'] = sequence_map[task_2]['parent_next_task']

    sequence_map[task_1]['child'] = sequence_map[task_2]['child']

    if sequence_map[task_2]['child_pre_task'] == task_1:
        sequence_map[task_1]['child_pre_task'] = task_2
    else:
        sequence_map[task_1]['child_pre_task'] = sequence_map[task_2]['child_pre_task']

    if sequence_map[task_2]['child_next_task'] == task_1:
        sequence_map[task_1]['child_next_task'] = task_2
    else:
        sequence_map[task_1]['child_next_task'] = sequence_map[task_2]['child_next_task']


    sequence_map[task_2]['parent'] = task_1_parent

    if task_1_parent_pre_task == task_2:
        sequence_map[task_2]['parent_pre_task'] = task_1
    else:
        sequence_map[task_2]['parent_pre_task'] = task_1_parent_pre_task

    if task_1_parent_next_task == task_2:
        sequence_map[task_2]['parent_next_task'] = task_1
    else:
        sequence_map[task_2]['parent_next_task'] = task_1_parent_next_task

    sequence_map[task_2]['child'] = task_1_child

    if task_1_child_pre_task == task_2:
        sequence_map[task_2]['child_pre_task'] = task_1
    else:
        sequence_map[task_2]['child_pre_task'] = task_1_child_pre_task

    if task_1_child_next_task == task_2:
        sequence_map[task_2]['child_next_task'] = task_1
    else:
        sequence_map[task_2]['child_next_task'] = task_1_child_next_task

    return Solution(solution.instance, sequence_map, path_init_task_map)

def neighbor_route_swap(solution):

    path_map = solution.get_path_map()

    chain_flag = random.randint(0, 1)

    if chain_flag == 0:
        M_list = Config.M_parent_list
    else:
        M_list = Config.M_child_list

    swap_path_index_list = random.sample(M_list, 2)
    path_1_index = swap_path_index_list[0]
    path_2_index = swap_path_index_list[1]

    path_map_temp = copy_dict_int_list(path_map)
    path_1 = path_map_temp[path_1_index]
    path_map_temp[path_1_index] = path_map_temp[path_2_index]
    path_map_temp[path_2_index] = path_1
    sequence_map, path_init_task_map = path_map2sequence_map(path_map_temp)
    return Solution(solution.instance, sequence_map, path_init_task_map)



def get_neighbor_solution(solution):
    neighbor_list = [neighbor_insertion, neighbor_swap, neighbor_route_swap]
    neighbor_index = random.randint(0,2)
    neighbor_solution = neighbor_list[neighbor_index](solution)
    return neighbor_solution



def DIWO(instance_name):
    instance = read_excel(instance_name)
    task_num = len(instance)

    weed_list = []

    best_solution = generate_solution_greedy(instance)
    best_fitness = best_solution.get_fitness()

    weed_list.append(best_solution)

    for _ in range(POP_INITIAL_SIZE - 1):
        init_solution = generate_solution_nearest(instance, random.random())
        weed_list.append(init_solution)

    start_time = time.time()
    count = 0

    duration = task_num ** 2 * (Config.M_child + Config.M_parent) * 10 / 1000

    # while time.time() - start_time < duration:
    while count < 100:
        count += 1
        fitness_list = [solution.get_fitness() for solution in weed_list]
        min_fitness = min(fitness_list)
        max_fitness = max(fitness_list)

        # 确保 best_solution 始终是当前找到的最佳解决方案
        # 如果当前 min_fitness 比之前记录的 best_fitness 更好，就更新 best_solution
        if min_fitness < best_fitness: # 或者使用 <= 取决于你如何定义“更好”
            best_fitness = min_fitness
            # 这里需要找到对应 min_fitness 的 solution 对象
            # 由于 weed_list 是经过排序的，best_solution 始终是 weed_seed_list[0]
            # 但是为了避免在循环中间遗漏，可以再确认一下
            # 实际上，在每次迭代结束时，best_solution = weed_seed_list[0] 已经更新了
            # 所以我们只需要确保在函数结束时返回的 best_solution 是最终的那个
            pass # 这一行可以不用改变，因为后面的 weed_seed_list[:POP_MAX_SIZE] 会保留最好的

        seed_list = []

        for weed_solution in weed_list:
            weed_fitness = weed_solution.get_fitness()

            '''获得每个weed产生seed的个数'''
            try:
                if abs(max_fitness - min_fitness) < 1e-5:
                    seed_num = random.randint(S_MIN, S_MAX)
                else:
                    seed_num = math.floor(S_MAX - (weed_fitness - min_fitness) / (max_fitness - min_fitness) * \
                                          (S_MAX - S_MIN))
            except:
                seed_num = random.randint(S_MIN, S_MAX)

            for seed_index in range(seed_num):
                new_solution = get_neighbor_solution(weed_solution)
                seed_list.append(new_solution)

        weed_seed_list = seed_list + weed_list
        weed_seed_list = sorted(weed_seed_list, key=lambda x: x.get_fitness())

        # 每次迭代结束后，更新 best_solution 为当前找到的最佳 solution 对象
        best_solution = weed_seed_list[0]

        weed_list = weed_seed_list[:POP_MAX_SIZE]

        # print(f"time: {time.time() - start_time} best_fitness: {best_fitness}")

    # 修改这里，返回 best_solution 对象
    return best_solution



def run_DIWO_experiment(instance_name, repeated):
    runtime_start_total = time.time()

    # Lists to store metrics for ALNS solutions
    costs = []
    delay_costs = []
    dist_costs = []


    best_cost = float('inf')  # Initialize with a very large number
    best_solution_code = None  # Stores the actual code/representation of the best ALNS solution

    print(f"Starting DIWO for instance: {instance_name} with {repeated} repetitions...")

    for i in range(repeated):
        print(f"--- Repetition {i + 1}/{repeated} ---")

        # Execute the ALNS algorithm for the current repetition
        solution = DIWO(instance_name)

        # Process ALNS solution metrics
        costs.append(solution.get_fitness())
        dist_costs.append(solution.distance)
        delay_costs.append(solution.tardiness)

        # Update best_alns_cost and best_alns_solution_code if the current ALNS solution is better
        if solution.get_fitness() < best_cost:
            best_cost = solution.get_fitness()
            best_solution_code = solution.get_code()  # Store the code of the best ALNS solution found so far

    total_runtime = time.time() - runtime_start_total
    avg_runtime_per_repetition = total_runtime / repeated

    # Calculate and package metrics for ALNS solutions
    data_package = _calculate_and_package_metrics(
        costs,
        dist_costs,
        delay_costs,
        avg_runtime_per_repetition
    )
    # Update the first element of alns_data_package with the best solution code
    data_package[0] = best_solution_code
    return best_solution_code, data_package



if __name__ == "__main__":
    # instance_name = "exp/hca/50/tasks_50_0"
    # solution = DIWO(instance_name)
    #
    # print(f"fitness:{solution.get_fitness()}")
    # print(f"distance:{solution.distance}  tardiness:{solution.tardiness}")
    data_package = run_DIWO_experiment("exp/hca/10/tasks_10_0.xlsx", 1)
    print(data_package)
    pass
